/*
 * Added MercuryPay by manit guragain (08/27/2012)
 */
package com.openbravo.pos.payment;

import com.openbravo.data.loader.LocalRes;
import com.openbravo.pos.forms.AppLocal;
import com.openbravo.pos.forms.AppProperties;
import com.openbravo.pos.htutils.DBTools;
import com.openbravo.pos.util.AltEncrypter;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import javax.xml.rpc.ServiceException;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

public class PaymentGatewayMercury implements PaymentGateway {

    private String MerchantID = "";
    private String WSPassword = "";
    private String mWebServicePath = "";
    private String mWebMethodName = "";
    private String mXMLNamespace = "";
    private int mTimeout = 30000;
    private String[][] mWSParams;
    private String WSHost = "";
    private boolean m_bTestMode;
    private String TransactionType = "Credit";
    private String TransactionCode = "PreAuth";
    private String WebMethodName = "CreditTransaction";
    private String XMLNamespace = "http://www.mercurypay.com";
    private boolean isTip;

    public PaymentGatewayMercury(AppProperties props) {
        // Initialize private variables
        MerchantID = props.getProperty("payment.commerceid");
        AltEncrypter cypher = new AltEncrypter("cypherkey" + props.getProperty("payment.commerceid"));
        this.WSPassword = cypher.decrypt(props.getProperty("payment.commercepassword").substring(6));
        this.m_bTestMode = Boolean.valueOf(props.getProperty("payment.testmode")).booleanValue();
        this.WSHost = (m_bTestMode)
                ? "https://ws1.mercurydev.net/ws/ws.asmx"
                //? "https://w1.mercurydev.net/ws/ws.asmx"
                : "https://w1.mercurypay.net/ws/ws.asmx";
        this.isTip = Boolean.parseBoolean(props.getProperty("machine.showTip"));
        if (!isTip) {
            //If no tip feature, directly process the transaction.
            TransactionCode = "Sale";
        }
    }
    //Default Constructor

    public PaymentGatewayMercury() {
        //Nothing much happening here 
    }

    public String doTransaction(String dsiXML, PaymentInfoMagcard payinfo) {
        try {
            MPSWebRequest comm = new MPSWebRequest();

            //TODO: Set Web Services password here
            String txnPassword = WSPassword;

            comm.setWebMethodName(WebMethodName);
            comm.setXMLNamespace(XMLNamespace);
            comm.setWebServiceURL(WSHost);
            comm.setParameter("tran", dsiXML);
            comm.setParameter("pw", txnPassword);
            String response = comm.sendRequest();
            return response;
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    @Override
    public void execute(PaymentInfoMagcard payinfo) {
        try {

            //Calculate Amount
            NumberFormat formatter = new DecimalFormat("0.00");
            String amount = formatter.format(Math.abs(payinfo.getTotal()));
            try {
                amount = URLEncoder.encode(amount.replace(',', '.'), "UTF-8");
            } catch (UnsupportedEncodingException ex) {
                System.out.println(ex.getMessage());
            }
            //TODO: get ticket id or sth like that instead of passing random numbers. 

            Random generator = new Random();
            int randomNum = generator.nextInt(100);
            String InvoiceNumber = String.valueOf(randomNum);

            String encryptedCard = payinfo.getEncryptedCardData();
            String encryptionKey = payinfo.getEncryptionKey();

            String xmlPayInfo = "";
            //Build Transaction XML
            //PAYMENT
            if (payinfo.getTotal() >= 0.0) {
                xmlPayInfo = "<?xml version=\"1.0\"?>"
                        + "<TStream>"
                        + "<Transaction>"
                        + "<MerchantID>" + MerchantID + "</MerchantID>"
                        + "<TranType>" + TransactionType + "</TranType>"
                        //TODO : remote this for live transaction                        
                        //+ "<OperatorID>Test</OperatorID>"

                        + "<TranCode>" + TransactionCode + "</TranCode>"
                        + "<InvoiceNo>" + InvoiceNumber + "</InvoiceNo>"
                        + "<RefNo>" + InvoiceNumber + "</RefNo>"
                        + "<Memo>FirstPaymentInt FPIoPOS v2.0.1</Memo>"
                        + "<PartialAuth>Allow</PartialAuth>"
                        + "<Frequency>OneTime</Frequency>"
                        + "<RecordNo>RecordNumberRequested</RecordNo>"
                        + "<Account>"
                        + "<EncryptedFormat>MagneSafe</EncryptedFormat>"
                        + "<AccountSource>Swiped</AccountSource>"
                        + "<EncryptedBlock>" + encryptedCard + "</EncryptedBlock>"
                        + "<EncryptedKey>" + encryptionKey + "</EncryptedKey>"
                        + "</Account>"
                        + "<Amount>"
                        + "<Purchase>" + amount + "</Purchase>"
                        + "<Authorize>" + amount + "</Authorize>"
                        + "</Amount>"
                        + "</Transaction>"
                        + "</TStream>";


            } //REFUND
            else {
                String initialTransactionID = payinfo.getTransactionID();
                DBTools db = new DBTools();
                String initialReturnedMessage = db.getReturnedMessage(initialTransactionID);
                //Parse the returned XML response
                parseSOAP pSoapRefund = new parseSOAP();
                //Collect the XML nodes in Map
                Map propsRefund = pSoapRefund.getPicklistFromSoapResponse(initialReturnedMessage);

                String refundAcqRefData = (String) propsRefund.get("AcqRefData");
                String refundRecordNo = (String) propsRefund.get("RecordNo");
                String refundProcessData = (String) propsRefund.get("ProcessData");
                String refundInvoiceNumber = (String) propsRefund.get("InvoiceNo");
                String refundRefNo = (String) propsRefund.get("RefNo");
                String AuthCode = (String) propsRefund.get("AuthCode");
                String refundAuthCode = (String) propsRefund.get("AuthCode");

                xmlPayInfo = "<?xml version=\"1.0\"?> "
                        + "<TStream>"
                        + "<Transaction>"
                        + "<MerchantID>" + MerchantID + "</MerchantID>"
                        + "<TranType>Credit</TranType> "
                        + "<TranCode>VoidSaleByRecordNo</TranCode>"
                        + "<InvoiceNo>" + refundInvoiceNumber + "</InvoiceNo> "
                        + "<RefNo>" + refundRefNo + "</RefNo>" //Required Reversal filler value RefNo=InvoiceNo 
                        + "<Memo>FirstPaymentInt FPIoPOS v2.0.1</Memo>"
                        + "<Account>"
                        + "<RecordNo>" + refundRecordNo + "</RecordNo>"//use Token RecordNo and Frequency here     
                        + "<Frequency>OneTime</Frequency>"
                        + "</Account>"
                        + "<Amount>"
                        + "<Purchase>" + amount + "</Purchase>"
                        + "</Amount>"
                        + "<TranInfo>";
                if (isTip) {
                    xmlPayInfo += "<AcqRefData>" + refundAcqRefData + "</AcqRefData>";
                    xmlPayInfo += "<ProcessData>" + refundProcessData + "</ProcessData>";
                }
                xmlPayInfo += "<AuthCode>" + refundAuthCode + "</AuthCode>"
                        + "</TranInfo>"
                        + "</Transaction>"
                        + "</TStream>";

            }
            //Submit Transaction to WebService and capture returned response

            String response = doTransaction(xmlPayInfo, payinfo);

            //Parse the returned XML response
            parseSOAP pSoap = new parseSOAP();

            //Collect the XML nodes in Map
            Map props = pSoap.getPicklistFromSoapResponse(response);

            String errorMsg = (String) props.get("TextResponse");

            if (pSoap.getResult().equals(LocalRes.getIntString("button.ok"))) {
                if ("approved".equals(props.get("CmdStatus").toString().trim().toLowerCase())) {
                    //Transaction approved
                    payinfo.paymentOK((String) props.get("AuthCode"), (String) props.get("AcqRefData"), response);
                } else {
                    //Transaction declined                    
                    payinfo.paymentError(AppLocal.getIntString("message.paymenterror"), errorMsg);
                }

            } else {
                //payinfo.paymentError(pSoap.getResult(), errorMsg);
            }
        } catch (ServiceException ex) {
            payinfo.paymentError(AppLocal.getIntString("message.paymenterror"), ex.getMessage());
        }
    }

    /*
     * Parse the SOAP XML to retrieve individual nodes by name and return Map.
     * (Key Value pari)
     */
    public class parseSOAP {

        private String result;

        public Map<String, String> getPicklistFromSoapResponse(String soapResponse) throws ServiceException {
            soapResponse = soapResponse.replaceFirst("^([\\W]+)<", "<");
            soapResponse = soapResponse.substring(soapResponse.indexOf("<?xml"));

            Map<String, String> values = new LinkedHashMap<String, String>();
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            String _CmdStatus = null;
            String _AuthCode = null;
            String _AcqRefData = null;
            String _TextResponse = null;
            String _RecordNo = null;
            String _ProcessData = null;
            String _InvoiceNumber = null;
            String _RefNo = null;
            try {
                InputStream in = new ByteArrayInputStream(soapResponse.getBytes("UTF-8"));
                XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
                while (eventReader.hasNext()) {
                    XMLEvent event = eventReader.nextEvent();
                    if (event.isStartElement()) {

                        if (event.asStartElement().getName().getLocalPart().equals("CmdStatus")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _CmdStatus = event.asCharacters().getData();
                            } else {
                                _CmdStatus = "";
                            }
                            values.put("CmdStatus", _CmdStatus);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("AuthCode")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _AuthCode = event.asCharacters().getData();
                            } else {
                                _AuthCode = "";
                            }
                            values.put("AuthCode", _AuthCode);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("AcqRefData")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _AcqRefData = event.asCharacters().getData();
                            } else {
                                _AcqRefData = "";
                            }
                            values.put("AcqRefData", _AcqRefData);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("TextResponse")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _TextResponse = event.asCharacters().getData();
                            } else {
                                _TextResponse = "";
                            }
                            values.put("TextResponse", _TextResponse);
                            continue;
                        }
                        //- - - - - - 
                        if (event.asStartElement().getName().getLocalPart().equals("RecordNo")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _RecordNo = event.asCharacters().getData();
                            } else {
                                _RecordNo = "";
                            }
                            values.put("RecordNo", _RecordNo);
                            continue;
                        }

                        if (event.asStartElement().getName().getLocalPart().equals("ProcessData")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _ProcessData = event.asCharacters().getData();
                            } else {
                                _ProcessData = "";
                            }
                            values.put("ProcessData", _ProcessData);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("InvoiceNo")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _InvoiceNumber = event.asCharacters().getData();
                            } else {
                                _InvoiceNumber = "";
                            }
                            values.put("InvoiceNo", _InvoiceNumber);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("RefNo")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _RefNo = event.asCharacters().getData();
                            } else {
                                _RefNo = "";
                            }
                            values.put("RefNo", _RefNo);
                            continue;
                        }


                    }
                }
            } catch (UnsupportedEncodingException e) {
                throw new ServiceException(e);

            } catch (XMLStreamException e) {
                throw new ServiceException(e);
            }
            result = LocalRes.getIntString("button.ok");
            return values;
        }

        public String getResult() {
            return this.result;
        }
    }
}